package com.ruoyi.web.controller.system;

import java.io.*;
import java.nio.file.Paths;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.system.mapper.VideoDataMapper;
import com.ruoyi.web.controller.utils.FileTypeCheckerUtil;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.VideoData;
import com.ruoyi.system.service.IVideoDataService;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;

/**
 * 数据管理Controller
 * 
 * @author wsx
 * @date 2025-07-30
 */
@Controller
@RequestMapping("/system/videoData")
public class VideoDataController extends BaseController {
    private String prefix = "system/videoData";

    @Value("${ffmpeg.savePath}")
    private String localVideoDir;

    @Autowired
    private IVideoDataService videoDataService;

    @Autowired
    private VideoDataMapper videoDataMapper;

    @RequiresPermissions("system:videoData:view")
    @GetMapping()
    public String videoData() {
        return prefix + "/videoData";
    }

    /**
     * 查询数据管理列表
     */
    // @RequiresPermissions("system:videoData:list")
    @PostMapping("/list")
    @ResponseBody
    public TableDataInfo list(VideoData videoData) {
        startPage();
        List<VideoData> list = videoDataService.selectVideoDataList(videoData);
        return getDataTable(list);
    }

    /**
     * 导出数据管理列表
     */
    // @RequiresPermissions("system:videoData:export")
    @Log(title = "数据管理", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    @ResponseBody
    public AjaxResult export(String ids, VideoData videoData) {
        List<VideoData> list = new ArrayList<>();
        if (StringUtils.isNotBlank(ids)) {
            list = videoDataService.selectVideoDataListByIds(ids, videoData);
        } else {
            list = videoDataService.selectVideoDataList(videoData);
        }
        ExcelUtil<VideoData> util = new ExcelUtil<VideoData>(VideoData.class);
        return util.exportExcel(list, "数据管理数据");
    }

    /**
     * 新增数据管理
     */
    @RequiresPermissions("system:videoData:add")
    @GetMapping("/add")
    public String add() {
        return prefix + "/add";
    }

    /**
     * 新增保存数据管理
     */
    // @RequiresPermissions("system:videoData:add")
    @Log(title = "数据管理", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    @ResponseBody
    public AjaxResult addSave(VideoData videoData) {
        return toAjax(videoDataService.insertVideoData(videoData)).put("dataId", videoData.getDataId());
    }

    /**
     * 修改数据管理
     */
    @RequiresPermissions("system:videoData:edit")
    @GetMapping("/edit/{dataId}")
    public String edit(@PathVariable("dataId") Long dataId, ModelMap mmap) {
        VideoData videoData = videoDataService.selectVideoDataByDataId(dataId);
        mmap.put("videoData", videoData);
        return prefix + "/edit";
    }

    /**
     * 修改保存数据管理
     */
    // @RequiresPermissions("system:videoData:edit")
    @Log(title = "数据管理", businessType = BusinessType.UPDATE)
    @PostMapping("/edit")
    @ResponseBody
    public AjaxResult editSave(VideoData videoData) {
        return toAjax(videoDataService.updateVideoData(videoData));
    }

    /**
     * 删除数据管理
     */
    // @RequiresPermissions("system:videoData:remove")
    @Log(title = "数据管理", businessType = BusinessType.DELETE)
    @PostMapping("/remove")
    @ResponseBody
    public AjaxResult remove(String ids) {
        return AjaxResult.success(videoDataService.updateDelFlag(ids));
    }

    /**
     * 下拉数据管理
     * 
     * @return
     */
    @PostMapping("/selectData")
    @ResponseBody
    public AjaxResult selectData() {
        VideoData videoData = new VideoData();
        videoData.setIsArchived("0");
        return AjaxResult.success(videoDataService.selectVideoDataList(videoData));
    }


    @GetMapping("/getStoragePath")
    @ResponseBody
    public AjaxResult getStoragePath(Long dataId) {
        // 1. 参数校验
        if (dataId == null) {
            return AjaxResult.error("数据ID不能为空");
        }

        // 2. 查询视频存储路径
        VideoData videoData = videoDataService.selectVideoDataByDataId(dataId);
        if (videoData == null) {
            return AjaxResult.error("未找到对应的视频数据");
        }

        String storagePath = videoData.getStoragePath();
        if (StringUtils.isEmpty(storagePath)) {
            return AjaxResult.error("视频存储路径不存在");
        }

        // 3. 读取目录下的文件
        File directory = new File(localVideoDir + storagePath);
        // 检查目录是否存在且为有效目录
        if (!directory.exists() || !directory.isDirectory()) {
            return AjaxResult.error("存储路径不存在或不是有效目录: " + storagePath);
        }

        // 4. 获取目录下的所有文件（不包含子目录）
        File[] files = directory.listFiles();
        if (files == null || files.length == 0) {
            return AjaxResult.success("目录下无文件", Collections.emptyList());
        }

        // 5. 用JSONArray+JSONObject封装文件信息（新增无后缀文件名）
        JSONArray fileInfoArray = new JSONArray();
        for (int i = 0; i < files.length; i++) {
            File file = files[i];
            JSONObject fileObj = new JSONObject();

            // 1. 序号ID（从1开始自增）
            fileObj.put("id", i + 1);

            // 2. 文件名（含后缀）
            String fileName = file.getName();
            fileObj.put("fileName", fileName);

            // 新增：不含后缀的文件名（核心修改）
            String fileNameWithoutSuffix = fileName;
            int lastDotIndex = fileName.lastIndexOf(".");
            if (lastDotIndex > 0) { // 确保存在"."且不在开头（如".gitignore"不截取）
                fileNameWithoutSuffix = fileName.substring(0, lastDotIndex);
            }
            fileObj.put("fileNameWithoutSuffix", fileNameWithoutSuffix);

            // 3. 文件大小（MB，保留2位小数）
            long fileSizeBytes = file.length();
            double fileSizeMB = fileSizeBytes / (1024.0 * 1024.0);
            fileObj.put("fileSize", Math.round(fileSizeMB * 100) / 100.0);
            fileObj.put("fileSizeUnit", "MB");

            // 4. 访问路径
            String absolutePath = file.getAbsolutePath().replace("\\", "/");
            String relativePath = absolutePath.substring(localVideoDir.length());
            String accessPath = "/file/" + relativePath;
            fileObj.put("accessPath", accessPath);

            // 5. 文件后缀名
            String fileSuffix = lastDotIndex > 0 ? fileName.substring(lastDotIndex + 1) : "";
            fileObj.put("fileSuffix", fileSuffix);

            // 6. 绝对路径
            fileObj.put("absolutePath", file.getAbsolutePath());

            fileInfoArray.add(fileObj);
        }

        // 6. 返回结果
        return AjaxResult.success("查询成功", fileInfoArray);
    }



    @GetMapping("/getTelemetryStoragePath")
    @ResponseBody
    public AjaxResult getTelemetryStoragePath(Long dataId) {
        // 1. 参数校验
        if (dataId == null) {
            return AjaxResult.error("数据ID不能为空");
        }

        // 2. 查询视频存储路径
        VideoData videoData = videoDataService.selectVideoDataByDataId(dataId);
        if (videoData == null) {
            return AjaxResult.error("未找到对应的视频数据");
        }

        // 拼接遥测存储路径（补充非空校验，避免拼接出无效路径）
        String telemetryRelativePath = videoData.getTelemetryStoragePath();
        if (StringUtils.isEmpty(telemetryRelativePath)) {
            return AjaxResult.error("视频遥测路径不存在");
        }
        String storagePath = "telemetryStoragePath/" + telemetryRelativePath;

        // 3. 读取目录下的文件（处理路径分隔符，避免Windows/Linux兼容问题）
        String absoluteDirPath = localVideoDir + storagePath.replace("/", File.separator);
        File directory = new File(absoluteDirPath);
        if (!directory.exists() || !directory.isDirectory()) {
            return AjaxResult.error("存储路径不存在或不是有效目录: " + absoluteDirPath);
        }

        // 4. 获取目录下的所有文件（不包含子目录）
        File[] files = directory.listFiles();
        if (files == null || files.length == 0) {
            return AjaxResult.success("目录下无文件", Collections.emptyList());
        }

        // 5. 封装文件信息（核心：文件大小改为KB）
        JSONArray fileInfoArray = new JSONArray();
        for (int i = 0; i < files.length; i++) {
            File file = files[i];
            JSONObject fileObj = new JSONObject();

            // 1. 序号ID
            fileObj.put("id", i + 1);

            // 2. 文件名（含后缀）
            String fileName = file.getName();
            fileObj.put("fileName", fileName);

            // 3. 不含后缀的文件名
            String fileNameWithoutSuffix = fileName;
            int lastDotIndex = fileName.lastIndexOf(".");
            if (lastDotIndex > 0) { // 避免后缀在开头（如 .gitignore），仅当点在中间时截取
                fileNameWithoutSuffix = fileName.substring(0, lastDotIndex);
            }
            fileObj.put("fileNameWithoutSuffix", fileNameWithoutSuffix);

            // 4. 文件大小（核心调整：从MB改为KB）
            long fileSizeBytes = file.length(); // 获取文件字节数
            // 字节转KB：1KB = 1024字节，保留1位小数（兼顾精度和可读性）
            double fileSizeKB = fileSizeBytes / 1024.0;
            fileObj.put("fileSize", Math.round(fileSizeKB * 10) / 10.0); // 四舍五入保留1位小数
            fileObj.put("fileSizeUnit", "KB"); // 单位改为KB

            // 5. 访问路径和存储路径（保持原逻辑，优化路径分隔符统一）
            String absolutePath = file.getAbsolutePath().replace(File.separator, "/"); // 统一转为/，便于前端处理
            String fileParentPath = file.getParentFile().getAbsolutePath().replace(File.separator, "/");
            // 截取相对于localVideoDir的目录路径（确保跨平台兼容）
            String relativeDirPath = fileParentPath.substring(localVideoDir.length()).replace(File.separator, "/");
            fileObj.put("savePath", relativeDirPath); // 目录路径（如telemetryStoragePath/20251007161519）
            // 访问路径：拼接固定前缀，统一格式
            String accessPath = "/file/" + absolutePath.substring(localVideoDir.length());
            fileObj.put("accessPath", accessPath);

            // 6. 文件后缀名（无后缀时返回空字符串）
            String fileSuffix = lastDotIndex > 0 ? fileName.substring(lastDotIndex + 1) : "";
            fileObj.put("fileSuffix", fileSuffix);

            // 7. 绝对路径（统一分隔符，便于前端查看）
            fileObj.put("absolutePath", absolutePath);

            fileInfoArray.add(fileObj);
        }

        // 6. 返回结果
        return AjaxResult.success("查询成功", fileInfoArray);
    }


    @PostMapping("saveTelemetryData")
    @ResponseBody
    public AjaxResult saveTelemetryData(@RequestBody JSONObject object){
        Map<String,Object> map=new HashMap<>();
        map.put("name",object.getString("name"));
        map.put("data_id",object.getString("dataId"));
        map.put("task_id",object.getString("taskId"));
        map.put("telemetry_task_id",object.getString("telemetryTaskId"));
        map.put("status",0);
        map.put("telemetry_data",object.getString("telemetryData"));
        videoDataMapper.insertTelemetryData(map);
        return AjaxResult.success("操作成功！");
    }


    /**
     * 批量新增遥测数据
     * @param dataList
     * @return
     */
    @PostMapping("batchSaveTelemetryData")
    @ResponseBody
    public AjaxResult batchSaveTelemetryData(@RequestBody JSONArray dataList) {
        // 验证输入是否为空
        if (dataList == null || dataList.isEmpty()) {
            return AjaxResult.error("批量导入数据不能为空");
        }
        try {
            // 创建批量插入的数据列表
            List<Map<String, Object>> batchList = new ArrayList<>(dataList.size());
            // 循环处理每条数据，添加到批量列表
            for (int i = 0; i < dataList.size(); i++) {
                JSONObject object = dataList.getJSONObject(i);
                Map<String, Object> map = new HashMap<>();
                // 设置与单个接口相同的参数映射
//                map.put("name", object.getString("name"));
//                map.put("data_id", object.getString("dataId"));
//                map.put("task_id", object.getString("taskId"));
                map.put("telemetry_task_id", object.getString("telemetryTaskId"));
                map.put("status", 0);
                map.put("telemetry_data", object.getString("telemetryData"));
                // 添加到批量列表
                batchList.add(map);
            }
            // 调用批量插入方法
            int insertCount = videoDataMapper.batchInsertTelemetryData(batchList);
            return AjaxResult.success("批量导入成功！共导入" + insertCount + "条数据");
        } catch (Exception e) {
            // 记录异常日志
            return AjaxResult.error("批量导入失败：" + e.getMessage());
        }
    }

    @Value(value = "${ffmpeg.savePath}")
    private String savePath;

    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");

    @PostMapping("generateTelemetryDataFile")
    @ResponseBody
    public AjaxResult generateTelemetryDataFile(@RequestBody JSONObject object) {
        if (object == null) {
            return AjaxResult.error("传入数据为空！生成失败！");
        }

        try {

            String telemetryDataSaveFileFolder = object.getString("telemetryDataSaveFileFolder");
            JSONArray dataList = object.getJSONArray("telemetryList");
            // 创建输出目录
//            String dateStr = LocalDateTime.now().format(DATE_FORMATTER);
//            String timeStr = LocalDateTime.now().format(TIME_FORMATTER);
            String absolutePath = savePath + "/telemetryStoragePath" + "/"+telemetryDataSaveFileFolder;

            //相对路径
            String relativePath = "/telemetryStoragePath" + "/" + telemetryDataSaveFileFolder;

            File dir = new File(absolutePath);

            // 确保目录存在
            if (!dir.exists()) {
                dir.mkdirs(); // 递归创建目录
            }

            // 处理每条数据
            for (int i = 0; i < dataList.size(); i++) {
                JSONObject dataObj = dataList.getJSONObject(i);
                Long taskId = dataObj.getLong("receivedTaskId");

                // 获取时间信息
                int hour = dataObj.getInteger("receivedSysTimeH");
                int minute = dataObj.getInteger("receivedSysTimeM");
                int second = dataObj.getInteger("receivedSysTimeS");
                int ms = dataObj.getInteger("receivedSysTimeMS");

                // 处理参数数据数组
                JSONArray paramArray = dataObj.getJSONArray("paramData");
                for (int j = 0; j < paramArray.size(); j++) {
                    JSONObject paramObj = paramArray.getJSONObject(j);
                    String paraId = paramObj.getString("paraID");
                    String paraName = paramObj.getString("paraName");
                    String paraValue = paramObj.getString("paraValue");

                    // 生成文件名：receivedTaskId_paraID_paraName.txt
                    String fileName = taskId + "_" + paraId + "_" + paraName + ".txt";
                    File dataFile = new File(dir, fileName);

                    // 构建要写入的内容
                    StringBuilder content = new StringBuilder();
                    // 时间格式：HH:MM:SS:MS
                    content.append(String.format("%02d:%02d:%02d:%03d", hour, minute, second, ms))
                            .append(" ")
                            .append(paraValue)
                            .append(System.lineSeparator()); // 换行

                    // 写入文件（追加模式）
                    try (FileWriter fw = new FileWriter(dataFile, true);
                         BufferedWriter bw = new BufferedWriter(fw)) {
                        bw.write(content.toString());
                    } catch (IOException e) {
                        throw new RuntimeException("写入文件失败：" + fileName, e);
                    }
                }
            }

            return AjaxResult.success("文件生成成功", relativePath);
        } catch (Exception e) {
            // 记录异常日志
            return AjaxResult.error("生成失败：" + e.getMessage());
        }
    }


    /**
     * 保存/取消 遥测数据
     * @param type 1为保存 0为删除
     * @param dataId 数据id ana_data表的主键
     * @param filePath generateTelemetryDataFile这个接口返回的相对路径
     * @return
     */
    @GetMapping("saveOrCancelTelemetry")
    @ResponseBody
    public AjaxResult saveOrCancelTelemetry(Integer type, Long dataId, String filePath) {

        try {
            if (type == 1) {
                // 2. 类型1：更新遥测路径
                videoDataMapper.updateTelemetryStoragePath(filePath, dataId);
            } else if (type == 0) {

                videoDataMapper.deleteStatusZeroData();
            } else {
                // 4. 无效type值校验
                return AjaxResult.error("参数错误：type只能为0（删除）或1（更新）！");
            }

            return AjaxResult.success("操作成功！");
        } catch (Exception e) {
            // 5. 异常捕获（记录日志便于排查）
            System.err.println("遥测保存/取消操作失败："+e);
            return AjaxResult.error("操作失败：" + e.getMessage());
        }
    }

    /**
     * 工具方法：递归删除目录（含目录下所有文件和子目录）
     * @param dir 待删除的目录
     * @return true=删除成功，false=删除失败
     */
    private boolean deleteDirRecursively(File dir) {
        // 1. 若目录下有子文件/子目录，先递归删除
        File[] files = dir.listFiles();
        if (files != null && files.length > 0) {
            for (File file : files) {
                // 1.1 若为子目录，继续递归删除
                if (file.isDirectory()) {
                    if (!deleteDirRecursively(file)) {
                        return false; // 子目录删除失败，直接返回
                    }
                } else {
                    // 1.2 若为文件，直接删除（判断删除结果）
                    if (!file.delete()) {
                        System.err.println("文件删除失败：" + file.getAbsolutePath());
                        return false;
                    }
                }
            }
        }
        // 2. 所有子内容删除完成后，删除当前目录
        boolean dirDeleteResult = dir.delete();
        if (!dirDeleteResult) {
            System.err.println("目录删除失败：" + dir.getAbsolutePath());
        }
        return dirDeleteResult;
    }


    /**
     * 编辑遥测数据
     * @param object
     * @return
     */
    @PostMapping("editTelemetryData")
    @ResponseBody
    public AjaxResult editTelemetryData(@RequestBody JSONObject object) {
        // 1. 核心配置：根存储路径（建议从配置文件注入，此处示例用savePath变量）
        String TEMP_SUB_DIR = savePath + "temporary";
        String dateDirName = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String finalTempDir = Paths.get(TEMP_SUB_DIR, dateDirName).toString();
        List<String> generatedFilePaths = new ArrayList<>();

        try {
            // -------------------------- 入参解析与校验 --------------------------
            String startDate = object.getString("startDate");
            String endDate = object.getString("endDate");
            JSONArray dataIds = object.getJSONArray("filePathList");

            if (startDate == null || startDate.trim().isEmpty()
                    || endDate == null || endDate.trim().isEmpty()
                    || dataIds == null || dataIds.isEmpty()) {
                return AjaxResult.error("参数错误：startDate、endDate、filePathList不能为空！");
            }

            // -------------------------- 关键修改：支持 HH:mm:ss:SSS 格式解析（兼容毫秒戳） --------------------------
            LocalTime startTime;
            LocalTime endTime;
            // 定义两种时间格式：1. HH:mm:ss:SSS（如16:04:26:969） 2. 毫秒时间戳（纯数字）
            DateTimeFormatter hmsSFormatter = DateTimeFormatter.ofPattern("HH:mm:ss:SSS");

            try {
                // 优先尝试解析 HH:mm:ss:SSS 格式（符合新需求的时间格式）
                startTime = LocalTime.parse(startDate.trim(), hmsSFormatter);
                endTime = LocalTime.parse(endDate.trim(), hmsSFormatter);
                System.out.println("使用 HH:mm:ss:SSS 格式解析时间：start=" + startTime + "，end=" + endTime);
            } catch (DateTimeParseException e1) {
                // 若不是 HH:mm:ss:SSS 格式，尝试解析为毫秒时间戳
                try {
                    long startMs = Long.parseLong(startDate.trim());
                    long endMs = Long.parseLong(endDate.trim());
                    startTime = getLocalTimeFromMilliseconds(startMs);
                    endTime = getLocalTimeFromMilliseconds(endMs);
                    System.out.println("使用毫秒时间戳解析时间：start=" + startTime + "，end=" + endTime);
                } catch (NumberFormatException e2) {
                    // 两种格式都不匹配，返回错误
                    return AjaxResult.error("时间格式错误！请传入 HH:mm:ss:SSS（如16:04:26:969）或毫秒时间戳");
                }
            }

            // -------------------------- 确保临时目录存在 --------------------------
            File tempDirFile = new File(finalTempDir);
            if (!tempDirFile.exists()) {
                boolean dirCreated = tempDirFile.mkdirs();
                if (!dirCreated) {
                    String errorMsg = "临时目录创建失败：" + finalTempDir;
                    System.err.println(errorMsg);
                    return AjaxResult.error(errorMsg);
                }
            }

            // -------------------------- 循环处理文件（时间筛选逻辑保持毫秒级精准） --------------------------
            for (int i = 0; i < dataIds.size(); i++) {
                JSONObject dataObj = dataIds.getJSONObject(i);
                String originalFilePath = dataObj.getString("path");
                File originalFile = new File(originalFilePath);

                // 原文件合法性校验
                if (!originalFile.exists() || !originalFile.isFile() || !originalFile.canRead()) {
                    System.err.println("原文件非法（不存在/非文件/无权限），跳过：" + originalFilePath);
                    continue;
                }

                // 定义新文件路径（finalTempDir + 原文件名_edit.txt）
                String originalFileName = originalFile.getName();
                String editFileName = originalFileName.replace(".txt", "_edit.txt");
                String editFileAbsolutePath = Paths.get(finalTempDir, editFileName).toString();
                File editFile = new File(editFileAbsolutePath);

                // 读取原文件并筛选（行内时间已为 HH:mm:ss:SSS，直接匹配格式）
                try (BufferedReader br = new BufferedReader(new FileReader(originalFile));
                     BufferedWriter bw = new BufferedWriter(new FileWriter(editFile))) {

                    String line;
                    while ((line = br.readLine()) != null) {
                        if (line.trim().isEmpty()) continue;

                        // 解析行内时间（格式 HH:mm:ss:SSS，取前12位，如"16:04:26:969"）
                        String timeStr = line.length() >= 12 ? line.substring(0, 12).trim() : "";
                        if (StringUtils.isEmpty(timeStr)) {
                            System.err.println("行无有效时间，跳过：" + line);
                            continue;
                        }

                        // 转换行内时间为 LocalTime（用相同的 HH:mm:ss:SSS 格式）
                        LocalTime lineTime;
                        try {
                            lineTime = LocalTime.parse(timeStr, hmsSFormatter); // 与入参解析格式一致
                        } catch (DateTimeParseException e) {
                            System.err.println("行时间格式异常（需HH:mm:ss:SSS），跳过：" + line);
                            continue;
                        }

                        // 筛选逻辑：精准到毫秒级（[startTime, endTime] 包含毫秒）
                        if ((lineTime.isAfter(startTime) || lineTime.equals(startTime))
                                && (lineTime.isBefore(endTime) || lineTime.equals(endTime))) {
                            bw.write(line);
                            bw.newLine();
                        }
                    }

                    generatedFilePaths.add(editFileAbsolutePath);
                    System.out.println("编辑文件生成成功：" + editFileAbsolutePath);

                } catch (IOException e) {
                    String errorMsg = "处理文件失败：" + originalFilePath + "，错误：" + e.getMessage();
                    System.err.println(errorMsg);
                    // 清理失败文件残留
                    if (editFile.exists() && !editFile.delete()) {
                        System.err.println("删除残留文件失败：" + editFileAbsolutePath);
                    }
                    continue;
                }
            }

            // -------------------------- 生成ZIP压缩包 --------------------------
            if (generatedFilePaths.isEmpty()) {
                return AjaxResult.error("未生成任何编辑文件（原文件均处理失败）");
            }

            // ZIP命名：包含毫秒时间戳，避免重复
            String zipFileName = "telemetry_edit_" + dateDirName + "_" + System.currentTimeMillis() + ".zip";
            String zipFileAbsolutePath = Paths.get(finalTempDir, zipFileName).toString();
            boolean zipSuccess = createZipFile(generatedFilePaths, zipFileAbsolutePath);
            if (!zipSuccess) {
                return AjaxResult.error("ZIP压缩包生成失败");
            }

            // -------------------------- 构造返回的ZIP访问路径 --------------------------
            // 修正路径拼接逻辑：避免 TEMP_SUB_DIR 包含根路径导致重复（仅保留 temporary/日期/文件名）
            String relativeTempDir = TEMP_SUB_DIR.replace(savePath, ""); // 去掉根路径，保留 temporary 部分
            String zipAccessPath = "/file/" + relativeTempDir + "/" + dateDirName + "/" + zipFileName;
            zipAccessPath = zipAccessPath.replace(File.separator, "/"); // 统一为URL分隔符

            return AjaxResult.success(
                    "处理完成！总文件数：" + dataIds.size() +
                            "，成功生成：" + generatedFilePaths.size() +
                            "，ZIP路径：" + zipAccessPath,
                    zipAccessPath);

        } catch (Exception e) {
            String errorMsg = "遥测数据编辑异常：" + e.getMessage();
            System.err.println(errorMsg);
            return AjaxResult.error(errorMsg);
        }
    }

    /**
     * 辅助方法：毫秒时间戳转 LocalTime（东八区，保留毫秒）
     * 例：1718000000969 -> 16:04:26.969
     */
    private LocalTime getLocalTimeFromMilliseconds(long milliseconds) {
        ZoneId zoneId = ZoneId.of("Asia/Shanghai");
        ZonedDateTime zonedDateTime = Instant.ofEpochMilli(milliseconds).atZone(zoneId);
        return zonedDateTime.toLocalTime(); // 自动保留毫秒（如16:04:26.969）
    }

    /**
     * 工具方法：将多个文件打包为ZIP
     * @param filePaths 待打包的文件绝对路径列表
     * @param zipFilePath 生成的ZIP文件绝对路径
     * @return true=打包成功，false=失败
     */
    private boolean createZipFile(List<String> filePaths, String zipFilePath) {
        try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFilePath))) {

            for (String filePath : filePaths) {
                File file = new File(filePath);
                if (!file.exists() || !file.isFile()) {
                    System.err.println("ZIP打包跳过不存在的文件：" + filePath);
                    continue;
                }

                // 读取文件内容并写入ZIP（ZIP内保留文件名，不保留上级路径）
                try (InputStream fileIn = new FileInputStream(file)) {
                    // ZIP条目：仅使用文件名（避免ZIP内出现多级目录）
                    ZipEntry zipEntry = new ZipEntry(file.getName());
                    zipOut.putNextEntry(zipEntry);

                    // 缓冲区写入（提升效率）
                    byte[] buffer = new byte[1024 * 8];
                    int len;
                    while ((len = fileIn.read(buffer)) != -1) {
                        zipOut.write(buffer, 0, len);
                    }

                    zipOut.closeEntry(); // 关闭当前ZIP条目
                    System.out.println("ZIP打包完成文件：" + file.getName());
                }
            }

            System.out.println("ZIP压缩包生成成功：" + zipFilePath);
            return true;

        } catch (IOException e) {
            System.err.println("ZIP打包失败：" + e.getMessage());
            // 若打包失败，删除残留ZIP文件
            File zipFile = new File(zipFilePath);
            if (zipFile.exists() && !zipFile.delete()) {
                System.err.println("删除失败ZIP残留：" + zipFilePath);
            }
            return false;
        }
    }


    /**
     * 上传遥测数据
     * @param request
     * @param file
     * @param path
     * @return
     * @throws IOException
     */
    @PostMapping("/uploadTxt")
    @ResponseBody
    public AjaxResult upload(HttpServletRequest request,
                             @RequestParam("file") MultipartFile[] files,
                             @RequestParam("path") String path) throws IOException {

        // 校验文件数组是否为空
        if (files == null || files.length == 0) {
            return AjaxResult.error("请选择需要上传的文件");
        }

        List<String> successFiles = new ArrayList<>();
        List<String> errorFiles = new ArrayList<>();

        // 生成统一时间戳（毫秒级），确保同一批次上传的文件时间戳一致
        long timestamp = System.currentTimeMillis();

        // 循环处理每个文件
        for (MultipartFile file : files) {
            if (file.isEmpty()) {
                errorFiles.add("空文件（未命名）");
                continue;
            }

            try {
                // 1. 校验文件类型（仅允许txt）
                if (!FileTypeCheckerUtil.isAllowedFileTypeTxt(file)) {
                    errorFiles.add(file.getOriginalFilename() + "（文件类型错误，仅支持txt）");
                    continue;
                }

                // 2. 构建上传目录绝对路径
                String absolutePath = localVideoDir + path + "/";

                // 3. 处理文件名（分离名称和后缀，并添加时间戳）
                String originalFileName = file.getOriginalFilename();
                if (originalFileName == null) {
                    errorFiles.add("未知文件名（无法解析）");
                    continue;
                }

                int lastDotIndex = originalFileName.lastIndexOf(".");
                String fileNameWithoutSuffix;
                String suffix = "";

                if (lastDotIndex > 0) {
                    fileNameWithoutSuffix = originalFileName.substring(0, lastDotIndex);
                    suffix = originalFileName.substring(lastDotIndex);
                } else {
                    fileNameWithoutSuffix = originalFileName;
                }

                // 核心修改：在文件名后添加"_时间戳"
                String fileNameWithTimestamp = fileNameWithoutSuffix + "_" + timestamp + suffix;

                // 4. 构建最终文件路径
                File filePath = new File(absolutePath + fileNameWithTimestamp);

                // 5. 确保目录存在
                if (!filePath.getParentFile().exists()) {
                    filePath.getParentFile().mkdirs();
                }

                // 6. 保存文件
                file.transferTo(filePath);

                // 7. 记录成功的文件访问路径（包含时间戳）
                String accessPath = "/file" + path + "/" + fileNameWithTimestamp;
                successFiles.add(accessPath);

            } catch (Exception e) {
                errorFiles.add(file.getOriginalFilename() + "（上传失败：" + e.getMessage() + "）");
            }
        }

        // 构建返回结果
        Map<String, Object> result = new HashMap<>();
        result.put("successCount", successFiles.size());
        result.put("errorCount", errorFiles.size());
        result.put("successFiles", successFiles);
        result.put("errorFiles", errorFiles);

        if (errorFiles.isEmpty()) {
            return AjaxResult.success("所有文件上传成功", result);
        } else if (successFiles.isEmpty()) {
            return AjaxResult.error("所有文件上传失败", result);
        } else {
            return AjaxResult.success("部分文件上传成功", result);
        }
    }



    @GetMapping("updateTelemetryData")
    @ResponseBody
    public AjaxResult updateTelemetryData(Integer type,Long dataId){

        if (type == 1) {
            videoDataMapper.updateStatusZeroTo1(dataId);
        }else {
            videoDataMapper.deleteStatusZeroData();
        }
        return AjaxResult.success("操作成功！");
    }

    @GetMapping("deleteTelemetryDataById")
    @ResponseBody
    public AjaxResult deleteTelemetryDataById(Long id){
        videoDataMapper.deleteTelemetryDataById(id);
        return AjaxResult.success("操作成功！");
    }


    @GetMapping("getTelemetryDataPageList")
    @ResponseBody
    public TableDataInfo getTelemetryDataPageList(Integer page, Integer limit,Integer dataId,Integer taskId,Integer telemetryId,String name){


        if (page != null && page>0) {
            page = page -1;
        }

        List<Map<String, Object>> list = videoDataMapper.getTelemetryDataPageList(taskId, telemetryId,dataId, name, page, limit);

        Long totalCount = videoDataMapper.getTelemetryDataTotalCount(taskId, telemetryId,dataId, name);

        TableDataInfo tableDataInfo = getDataTable(list);
        tableDataInfo.setTotal(totalCount);

        return tableDataInfo;
    }


}
